﻿using CPAIS.Model;
using System;
using System.Xml;
using System.Collections.Generic;

namespace CPAIS.LogParser.PluginsWithXmlReader
{
    /// <summary>
    /// Log parser for parsing event log file with XML Root TAG 'Eventlog', like:
    /// 
    /// <Eventlog>
    ///     <Case CP_ID="5" Visit_ID="2" OutPatient_ID="10160" InPatient_ID="364780" Diagnosis_Date="2008-06-13T16:57:43.000" Admit_Date="2008-06-13T16:57:43.000" Discharge_Date="2008-06-18T16:00:00.000" BirthDay="1930-01-10T00:00:00.000" Sex="女" Department="心内监护室" Cost="3263.17">
    ///         <Diagnosis>
    ///             <Diagnosis Diagnosis=" 冠状动脉粥样硬化性心脏病" ID="D1" />
    ///         </Diagnosis>
    ///         <Case>
    ///             <Event ID="OS33" Day="0" />
    ///             <Event ID="A5" Day="4" />
    ///         </Case>
    ///     </Case>
    /// </Eventlog>
    /// 
    /// </summary>
    public class LogParserA : ILogParserWithXmlReader
    {
        #region String used for parsing XML file
        private const string EventLog = "Eventlog";
        private const string Case = "Case";
        private const string Visit_ID = "Visit_ID";
        private const string InPatient_ID = "InPatient_ID";
        private const string Admit_Date = "Admit_Date";
        private const string Discharge_Date = "Discharge_Date";
        private const string BirthDay = "BirthDay";
        private const string Sex = "Sex";
        private const string Department = "Department";
        private const string Cost = "Cost";
        private const string Diagnosis = "Diagnosis";
        private const string Event = "Event";
        private const string ID = "ID";
        private const string Day = "Day";

        private const string Male = "男";
        private const string Female = "女";
        private const char Observ = 'O';
        private const char Action = 'A';

        private const string KnowledgeBase = "KnowledgeBase";
        private const string Context = "Context";
        private const string Id_Index = "Id_Index";
        private const string Type = "Type";
        private const string Col_Name = "Col_Name";
        #endregion

        private class KnowledgeBaseItem
        {
            public string Id_Index;
            public string Type;
            public string ID;
            public string Col_Name;
        }

        private Dictionary<string, KnowledgeBaseItem> knowledgeBase;
        public LogParserA()
        {
            knowledgeBase = new Dictionary<string, KnowledgeBaseItem>();
        }

        public string XmlTag
        {
            get { return EventLog; }
        }

        public void Parse(Model.LogModel logModel, System.Xml.XmlReader reader, Updater.StateUpdater updater)
        {
            while (reader.Read())
            {
                #region Depth 1
                if (reader.Depth < 1)
                    break;
                else if (reader.Depth == 1 && reader.NodeType == XmlNodeType.Element && reader.Name == KnowledgeBase)
                {
                    while (reader.Read())
                    { 
                        if (reader.Depth < 2)
                            break;
                        else if (reader.Depth == 2 && reader.NodeType == XmlNodeType.Element && reader.Name == Context)
                        {
                            KnowledgeBaseItem item = new KnowledgeBaseItem();
                            while (reader.MoveToNextAttribute())
                            {
                                if (reader.Name == Id_Index)
                                    item.Id_Index = reader.ReadContentAsString().Trim();
                                else if (reader.Name == Type)
                                    item.Type = reader.ReadContentAsString().Trim();
                                else if (reader.Name == ID)
                                    item.ID = reader.ReadContentAsString().Trim();
                                else if (reader.Name == Col_Name)
                                    item.Col_Name = reader.ReadContentAsString().Trim();
                            }
                            knowledgeBase.Add(item.Id_Index, item);
                        }
                    }
                }
                else if (reader.Depth == 1 && reader.NodeType == XmlNodeType.Element && reader.Name == Case)
                {
                    #region Read Patient Attributes

                    string
                        strVistID = "",
                        strInPatientID = "",
                        strSex = "",
                        strDepartment = "",
                        strCost = "";
                    DateTime
                        admitTime = new DateTime(),
                        dischargeTime = new DateTime(),
                        birthday = new DateTime();

                    while (reader.MoveToNextAttribute())
                    {
                        switch (reader.Name)
                        {
                            case InPatient_ID:
                                strInPatientID = reader.ReadContentAsString().Trim();
                                break;
                            case Visit_ID:
                                strVistID = reader.ReadContentAsString().Trim();
                                break;
                            case Sex:
                                strSex = reader.ReadContentAsString().Trim();
                                break;
                            case Department:
                                strDepartment = reader.ReadContentAsString().Trim();
                                break;
                            case Cost:
                                strCost = reader.ReadContentAsString().Trim();
                                break;
                            case Admit_Date:
                                admitTime = reader.ReadContentAsDateTime();
                                break;
                            case Discharge_Date:
                                dischargeTime = reader.ReadContentAsDateTime();
                                break;
                            case BirthDay:
                                birthday = reader.ReadContentAsDateTime();
                                break;
                        }
                    }
                    Trace trace = new Trace(logModel, strInPatientID + "_" + strVistID);
                    trace.Sex = strSex == Male ? SexType.Male : (strSex == Female ? SexType.Female : SexType.Unknown);
                    trace.Department = strDepartment;
                    trace.Cost = strCost == "" ? 0.0 : double.Parse(strCost);
                    trace.Admit = admitTime;
                    trace.Discharge = dischargeTime;
                    trace.Birthday = birthday;

                    #endregion

                    while (reader.Read())
                    {
                        #region Depth 2
                        if (reader.Depth < 2)
                            break;
                        else if (reader.Depth == 2 && reader.NodeType == XmlNodeType.Element)
                        {
                            if (reader.Name == Diagnosis)
                            {
                                while (reader.Read())
                                {
                                    #region Depth 3
                                    if (reader.Depth < 3)
                                        break;
                                    else if (reader.Depth == 3 && reader.NodeType == XmlNodeType.Element)
                                    {
                                        if (reader.Name == Diagnosis)
                                        {
                                            while (reader.MoveToNextAttribute())
                                            {
                                                if (reader.Name == Diagnosis)
                                                {
                                                    trace.AddDiagnosis(reader.ReadContentAsString().Trim());
                                                }
                                            }
                                        }
                                    }
                                    #endregion
                                }
                            }
                            else if (reader.Name == Case)
                            {
                                while (reader.Read())
                                {
                                    #region Depth 3
                                    if (reader.Depth < 3)
                                        break;
                                    else if (reader.Depth == 3 && reader.NodeType == XmlNodeType.Element)
                                    {
                                        if (reader.Name == Event)
                                        {
                                            string typeName = "";
                                            int day = 0;
                                            while (reader.MoveToNextAttribute())
                                            {
                                                if (reader.Name == ID)
                                                {
                                                    typeName = reader.ReadContentAsString().Trim();
                                                }
                                                else if (reader.Name == Day)
                                                {
                                                    day = reader.ReadContentAsInt();
                                                }
                                            }
                                            if (typeName[0] == Observ)
                                            {
                                                ObservationEvent entry = new ObservationEvent(trace, day, typeName);
                                                trace.AddObservation(entry);
                                            }
                                            else if (typeName[0] == Action)
                                            {
                                                // Map the action name
                                                if (knowledgeBase.ContainsKey(typeName))
                                                    typeName = knowledgeBase[typeName].ID;

                                                ActionEvent entry = new ActionEvent(trace, day, typeName);
                                                trace.AddAction(entry);
                                            }
                                        }
                                    }
                                    #endregion
                                }
                            }
                        }
                        #endregion
                    }
                    if (knowledgeBase.ContainsKey("A0"))
                        trace.AddAction(new ActionEvent(trace, 0, knowledgeBase["A0"].ID));
                    else
                        trace.AddAction(new ActionEvent(trace, 0, "A0"));
                    trace.OrderTrace();
                    logModel.AddTrace(trace);
                    if (updater != null)
                        updater.UpdateMessage(string.Format("{0} traces have been loaded...", logModel.TraceCount));
                }
                #endregion
            }
        }
    }
}